﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Args;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;
using Microsoft.WindowsAzure.ServiceRuntime;
using System.ComponentModel;
using System.Diagnostics;

namespace XDriveMount
{
    class Program
    {
        static AzureArgumentsParser parser = new AzureArgumentsParser();

        static void Main(string[] args)
        {
            CloudStorageAccount account;

            //parse each argument in context of Windows Azure first
            var parsed = args.Select(s => parser.ParseArguments(s)).ToArray();

            var config = Args.Configuration.Configure<CommandArgs>();

            var cmd = config.AsFluent()
                             .UsingStringComparer(StringComparer.InvariantCultureIgnoreCase)
                             .UsingSwitchDelimiter("/")
                             .Initialize()
                             .CreateAndBind(parsed);

            switch (cmd.OptionCommand)
            {
                case Option.Mount:
                    if (cmd.StorageConnection == null)
                    {
                        Console.WriteLine("Must specify the /sc switch for the mount option");
                        break;
                    }

                    account = EnsureHttpAccount(cmd.StorageConnection);
                    
                    var drive = GetCloudDrive(cmd.DriveURL, account);
                    Trace.TraceInformation("Mounting {0}", drive.Uri);

                    var cache = SetCache(cmd.CacheName);

                    if (cmd.Create)
                    {
                        Trace.TraceInformation("Creating new drive");
                        drive.CreateIfNotExist(cmd.SizeInMb > 0 ? cmd.SizeInMb : 1024);
                    }

                    Trace.TraceInformation("Starting {0} Mount", cmd.Force ? "Force" : "Normal");
                    var letter = drive.Mount(
                        (cache.MaximumSizeInMegabytes - 20),
                        cmd.Force ? DriveMountOptions.Force : DriveMountOptions.None
                        );

                    Console.WriteLine(letter);

                    break;
                case Option.UnMount:
                    account = EnsureHttpAccount(cmd.StorageConnection);
                    var driveUrl = account.CreateCloudBlobClient().GetBlobReference(cmd.DriveURL).Uri;

                    var drives = CloudDrive.GetMountedDrives().Where(d => d.Value.Equals(driveUrl));

                    foreach (var md in drives)
                    {
                        Trace.TraceInformation("Unmounting {0} from {1}", md.Key, md.Value);
                        new CloudDrive(md.Value, account.Credentials).Unmount();
                    }

                    break;
                case Option.UnMountAll:
                    account = EnsureHttpAccount(cmd.StorageConnection);

                    foreach (var md in CloudDrive.GetMountedDrives())
                    {
                        Trace.TraceInformation("Unmounting {0} from {1}", md.Key, md.Value);
                        new CloudDrive(md.Value, account.Credentials).Unmount();
                    }
                    break;
                case Option.MountSnapshot:
                    account = EnsureHttpAccount(cmd.StorageConnection);

                    var snapshotUrl = GetCloudDrive(cmd.DriveURL, account).Snapshot();
                    drive = GetCloudDrive(snapshotUrl, account);

                    var snapshotCache = SetCache(cmd.CacheName);

                    Trace.TraceInformation("Starting {0} Mount", cmd.Force ? "Force" : "Normal");
                    var snapshotLetter = drive.Mount(
                        (snapshotCache.MaximumSizeInMegabytes - 20),
                        cmd.Force ? DriveMountOptions.Force : DriveMountOptions.None
                        );

                    Console.WriteLine(snapshotLetter);
                    break;
                case Option.List:
                    foreach (var md in CloudDrive.GetMountedDrives())
                    {
                        Trace.TraceInformation("{0} : {1}", md.Key, md.Value);
                    }
                    break;
                default:
                    break;
            }
        }

        private static CloudDrive GetCloudDrive(string driveUrl, CloudStorageAccount account)
        {
            var blob = account.CreateCloudBlobClient().GetBlobReference(driveUrl);
            return new CloudDrive(blob.Uri, account.Credentials);
        }

        private static CloudDrive GetCloudDrive(Uri driveUrl, CloudStorageAccount account)
        {
            return new CloudDrive(driveUrl, account.Credentials);
        }

        private static LocalResource SetCache(string cacheLocalResourceName)
        {
            var cache = RoleEnvironment.GetLocalResource(cacheLocalResourceName);
            Trace.TraceInformation("Using {0} drive cache", cache.Name);
            CloudDrive.InitializeCache(cache.RootPath, cache.MaximumSizeInMegabytes);

            return cache;
        }

        private static CloudStorageAccount EnsureHttpAccount(string connectString)
        {
            CloudStorageAccount account;

            if (CloudStorageAccount.TryParse(connectString, out account))
            {
                return new CloudStorageAccount((StorageCredentialsAccountAndKey)account.Credentials, false);
            }
            throw new ArgumentException("Invalid Storage ConnectionString");
        }
    }

    class CommandArgs
    {
        [ArgsMemberSwitch("url")]
        public string DriveURL { get; set; }
        [ArgsMemberSwitch("sc")]
        public string StorageConnection { get; set; }
        [ArgsMemberSwitch(0)]
        [DefaultValue(Option.List)]
        public Option OptionCommand { get; set; }
        [ArgsMemberSwitch("cacheName")]
        public string CacheName { get; set; }
        public bool Force { get; set; }
        public bool Create { get; set; }
        public int SizeInMb { get; set; }
    }

    enum Option
    {
        List,
        Mount,
        UnMount,
        UnMountAll,
        MountSnapshot
    }
}
